package server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.LinkedList;
import java.util.ListIterator;

public class Client implements Runnable{
	Socket socket;
	PrintWriter out;
	BufferedReader in;
	LinkedList<File> files;
	ClientFileCatalog catalog;
	
	
	
	/**
	 * Default constructor
	 * @param socket socket that is created byt accepting client connection on ServerSocket
	 * @param catalog reference to catalog
	 * @throws UnsupportedEncodingException Communication is made with incorrect encoding
	 * @throws IOException failed to get InputStream or OutputStream from socket
	 */
	public Client(Socket socket, ClientFileCatalog catalog) throws UnsupportedEncodingException, IOException{
		files = new LinkedList<File>();
		out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "ISO-8859-1"), true);
		in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		//this.controller = controller;
		this.catalog = catalog;
		this.socket = socket;
		Thread thread = new Thread(this);
		thread.start();
	}

	/**
	 * Constantly listens for messages from client.
	 * Calls for removal of client on lost connection.
	 */
	@Override
	public void run() {
		try {
			String message;
			while((message = in.readLine()) != null){
				interpretMessage(message);
			}
		} catch (IOException e) {
			catalog.removeClient(this);
		}
	}
	
	/**
	 * Gets InetAddress that socket is connected to
	 * @return inetAddress that socket is connected to
	 */
	public InetAddress getAddress(){
		return socket.getInetAddress();		
	}
	
	/**
	 * Sends message to client
	 * @param message String message to be sent
	 */
	public void messageToClient(String message){
		out.println(message);
		out.flush();
	}
	
	/**
	 * Gets port number that socket is connected to
	 * @return port number
	 */
	public int getPort(){
		return socket.getPort();
	}
	
	/**
	 * Interprets message and calls the correct methods
	 * @param message message to be interpreted
	 */
	private void interpretMessage(String message){
			String command = getMessageCommand(message);
			
			if (command.equalsIgnoreCase("register")){
				//controller.registerFiles(getMessageContent(message), this);
				storeFiles(getMessageContent(message));
			} else if (command.equalsIgnoreCase("request")){
				String result = catalog.searchFor(getMessageContent(message), this);
				messageToClient(result);
			} else if (command.equalsIgnoreCase("unregister")){
				try {
					socket.close();
				} catch (IOException e) {}
				//controller.disconnectClient(this);
			} else {
				System.out.println("Client@" + this.getAddress() + " sent an unknown command: " + message);
			}
	}
	
	
	/**
	 * Gets content information by removing protocol specific info
	 * @param message Message
	 * @return String with message content
	 */
	private String getMessageContent(String message){
		String[] stringArray = message.split(":");
		return stringArray[1];
	}
	
	/**
	 * Gets protocol specific info
	 * @param message Message
	 * @return String with protocol message
	 */
	private String getMessageCommand(String message){
		String[] stringArray = message.split(":");
		return stringArray[0];
	}

	/**
	 * Add files in String to list
	 * @param fileString String with file information
	 */
	public void storeFiles(String fileString){
		String[] fileNameArray = fileString.split("#");
		synchronized (files){
			files = new LinkedList<File>(); 
			for (int i = 0; i < fileNameArray.length; i++){
				files.add(new File(fileNameArray[i], this));
			}
		}
		StringBuilder listOfFilesForPrint = new StringBuilder();
		listOfFilesForPrint.append("Client@" + this.getAddress() + " added the following files:");
		listOfFilesForPrint.append(System.getProperty("line.separator"));
		for (int i = 0; i < fileNameArray.length; i++){
			String space = ". ";
			if ((i+1)<10)
				space += " ";
			if ((i+1)<100)
				space += " ";
			listOfFilesForPrint.append((i+1) + space + fileNameArray[i] + System.getProperty("line.separator"));
		}
		System.out.print(listOfFilesForPrint);
	}
	
	/**
	 * Searches list of shared files for filenames matching keyword
	 * @param keyword keyword to match against filenames
	 * @return List of matching files
	 */
	public LinkedList<File> searchFor(String keyword){
		LinkedList<File> hits = new LinkedList<File>();
		if (files != null){
			ListIterator<File> iter = files.listIterator();
			while(iter.hasNext()){
				File file = iter.next();
				if (file.toString().toLowerCase().contains(keyword.toLowerCase())){
					hits.add(file);
				}
			}
		}
		return hits;
	}
	/**
	 * Number of files shared in total
	 * @return files shared by all clients
	 */
	public int getNumberOfFiles(){
		if (files == null){
			return 0;
		} else {
			return files.size();
		}
	}
}
